// @Author EthanScriptOn
// @Desc
package holder

import (
	"gitee.com/fatzeng/srf_switch_basic_components/common/custom_exp"
	"gitee.com/fatzeng/srf_switch_basic_components/util"
	"sync"
	"time"
)

type RecordHolder struct {
	StateHolder *StateHolder
	LogHolder   *LogHolder
}

func GenerateRecordHolder(timeUnit ExecuteTimeUnit) *RecordHolder {
	return &RecordHolder{StateHolder: GenerateStateHolder(timeUnit), LogHolder: GenerateLogHolder()}
}

type StateHolder struct {
	ExecutionState *ExecutionState
	ExecutionTime  *ExecutionTime
}

func GenerateStateHolder(timeUnit ExecuteTimeUnit) *StateHolder {
	return &StateHolder{ExecutionState: NewExecutionStateDefault(), ExecutionTime: NewExecutionTime(timeUnit)}
}

func (s *StateHolder) RecordCheckTimeOut(state ExecuteState, reason string) {
	s.Record(state, reason, true)
}

func (s *StateHolder) Record(state ExecuteState, reason string, checkTimeOut bool) {
	s.ExecutionTime.TimeRecord()
	if checkTimeOut && s.ExecutionTime.IsTimeOut() {
		s.timeOut()
		return
	}
	s.ExecutionState.StateRecord(state, reason)
}

func (s *StateHolder) Success() {
	s.RecordCheckTimeOut(EXECUTE_SUCCESS, "Success")
}

func (s *StateHolder) Fail(reason string) {
	s.Record(EXECUTE_FAIL, reason, false)
}

func (s *StateHolder) Interrupt(reason string) {
	s.Record(EXECUTE_INTERRUPT, reason, false)
}

func (s *StateHolder) timeOut() {
	s.ExecutionState.StateRecord(EXECUTE_TIME_OUT, "TimeOut")
}

func (s *StateHolder) TimeOut() {
	s.Record(EXECUTE_TIME_OUT, "TimeOut", false)
}

func (e *ExecutionState) StateRecord(state ExecuteState, reason string) {
	e.ExecuteState = state
	e.Reason = reason
}

func (e *ExecutionTime) TimeRecord() {
	e.FinishTime = time.Now()
	e.ExecuteTime = e.FinishTime.Sub(e.FinishTime)
}

func (e *ExecutionTime) IsTimeOut() bool {
	return e.ExecuteTime > e.ExpectedTime
}

func (r *RecordHolder) Record(err *error) {
	if err == nil {
		r.StateHolder.Success()
		return
	}
	recordError := *err
	errStr := recordError.Error()
	r.LogHolder.LogRecord(errStr)
	switch recordError.(type) {
	case custom_exp.InterruptError:
		r.StateHolder.Interrupt(errStr)
	case custom_exp.FailError:
		r.StateHolder.Fail(errStr)
	case custom_exp.TimeOutError:
		r.StateHolder.TimeOut()
	}
}

type LogHolder struct {
	ExecutionLog []string
	mu           *sync.Mutex
}

func GenerateLogHolder() *LogHolder {
	return &LogHolder{ExecutionLog: make([]string, 0), mu: &sync.Mutex{}}
}

func (l *LogHolder) LogRecord(log string) {
	_ = util.LockWithSync(l.mu.Lock, l.mu.Unlock, func() (err error) {
		l.ExecutionLog = append(l.ExecutionLog, log)
		return
	})
}
